iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 18
0
自我挑戰組

寫遊戲初體驗系列 第 18

Day 18 Shader Class

  • 分享至 

  • xImage
  •  

自己的 Shader

從前面的經驗知道,編寫、編譯以及管理 Shader 不是一件容易的事,如果我們將他封裝起來,讓這個管理器能夠從硬碟讀取 Shader,接著編譯再 Link 起來,這樣使用起來會方便許多。

#pragma once

#include "pch.h"
#include <glad/glad.h>
#include "glm/glm.hpp"
#include "glm/glm.hpp"

class Shader {
public:
    Shader() = default;
    Shader(const char *vertPath, const char *fragPath);

    ~Shader();

    void bind();

    void unbind();

    uint32_t getShaderID() const { return program; }

    int getLocationByName(const std::string &name) const;

    void setInt(const std::string &name, int value);

    void setIntArray(const std::string &name, int *values, uint32_t count);

    //
    void setFloat(const std::string &name, float value);

    void setFloat2(const std::string &name, const glm::vec2 &value);

    void setFloat3(const std::string &name, const glm::vec3 &value);

    void setFloat4(const std::string &name, const glm::vec4 &value);

    //
    void setMat2(const std::string &name, const glm::mat2 &matrix);

    void setMat3(const std::string &name, const glm::mat3 &matrix);

    void setMat4(const std::string &name, const glm::mat4 &matrix);


    static uint32_t CompileShader(GLenum type, const char **src);

    static uint32_t LinkShaderProgram(uint32_t vertex, uint32_t fragment);

private:
    std::string vertSource, fragSource;
    uint32_t program = 0;
};

program 儲存的其實就是此 Shader 的 ID,而我可們 Construct 的時候可以傳入 Vertex Shader 跟 fragment Shader 的檔案路徑,這樣我們就可以不用把 Shader 的 code 寫在程式裡面了。
我們還加入了一些功能。bind就是用來激活 Shader的,set ...就是用來設定uniform參數的。

讀檔

用 C++ 讀檔將 Shader 內容儲存到 std::string

  • 讀檔

    bool LoadFileContent(std::string &s, const char *path) {
        std::ifstream ifs(path);
        if (ifs) {
            s.assign((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
            return true;
        }
        return false;
    }
    
  • 接著要編譯並連結

    Shader::Shader(const char *vertPath, const char *fragPath) {
    
        assert(LoadFileContent(vertSource, vertPath) == true);
        assert(LoadFileContent(fragSource, fragPath) == true);
    
        const char *vertSrc = vertSource.c_str();
        uint32_t vert = CompileShader(GL_VERTEX_SHADER, &vertSrc);
        const char *fragSrc = fragSource.c_str();
        uint32_t frag = CompileShader(GL_FRAGMENT_SHADER, &fragSrc);
        assert(vert && frag);
    
        program = LinkShaderProgram(vert, frag);
        assert(program);
    
        glDeleteShader(vert);
        glDeleteShader(frag);
    }
    
    • 編譯
    uint32_t Shader::CompileShader(GLenum type, const char **src) {
    
        uint32_t shader;
        shader = glCreateShader(type);
        glShaderSource(shader, 1, src, nullptr);
        glCompileShader(shader);
    
        int success;
        char log[512];
        glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
        if (!success) {
            glGetShaderInfoLog(shader, 512, nullptr, log);
            printf("Error Shader %s compile error\n%s\n", type == GL_VERTEX_SHADER ? "Vertex" : "Fragment", log);
            return 0;
        }
        return shader;
    }
    
    • 連結
    uint32_t Shader::LinkShaderProgram(uint32_t vertex, uint32_t fragment) {
        uint32_t program = glCreateProgram();
        glAttachShader(program, vertex);
        glAttachShader(program, fragment);
        glLinkProgram(program);
        //
        int success;
        char log[512];
        glGetProgramiv(program, GL_LINK_STATUS, &success);
        if (!success) {
            glGetProgramInfoLog(program, 512, nullptr, log);
            printf("Error Shader Linking error\n%s\n", log);
            return 0;
        }
        return program;
    }
    
  • bind/unbind 非常之簡單。

    void bind() {
        glUseProgram(program);
    }
    
    void unbind() {
        glUseProgram(0);
    }
    
  • setter也是很簡單

    void Shader::setInt(const std::string &name, int value) {
        glUniform1i(getLocationByName(name), value);
    }
    
    void Shader::setIntArray(const std::string &name, int *values, uint32_t count) {
        glUniform1iv(getLocationByName(name), count, values);
    }
    
    void Shader::setFloat(const std::string &name, float value) {
        glUniform1f(getLocationByName(name), value);
    }
    
    void Shader::setFloat2(const std::string &name, const glm::vec2 &value) {
        glUniform2f(getLocationByName(name), value.x, value.y);
    }
    

這樣就成功封裝我們的 Shader 了

使用方法會像這樣

Shader shader("vertexShaderPath", "fragmentShaderPath");

...

while(running) {
    
    shader.bind();
    shader.setXX("uniform", value);
    render();
    shader.unbind();
}

上一篇
Day 17 ImGui 元件
下一篇
Day 19 Texture & ResourceManager
系列文
寫遊戲初體驗30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言